package com.icloud.system.boot.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.icloud.common.cache.constants.CacheConstants;
import com.icloud.common.cache.service.RedisService;
import com.icloud.common.core.api.CommonResult;
import com.icloud.common.core.api.ResultCode;
import com.icloud.common.core.utils.RandomValueUtils;
import com.icloud.common.database.enums.*;
import com.icloud.common.database.mapper.*;
import com.icloud.common.database.model.*;
import com.icloud.common.web.constants.CommonConstants;
import com.icloud.common.web.constants.UrlConstants;
import com.icloud.common.web.enums.StateEnum;
import com.icloud.common.web.exception.ApiException;
import com.icloud.common.web.exception.Asserts;
import com.icloud.common.web.util.AuthUtil;
import com.icloud.system.boot.dto.AddAppCustomerDto;
import com.icloud.system.boot.dto.UpdateAppCustomerDto;
import com.icloud.system.boot.mapper.*;
import com.icloud.system.boot.model.*;
import com.icloud.system.boot.service.AppCustomerService;
import com.icloud.system.boot.vo.AddAppCustomerVo;
import com.icloud.system.boot.vo.CustomerInvitePathVo;
import com.icloud.system.boot.vo.ProfileVo;
import com.icloud.system.boot.vo.SysSettingsVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AppCustomerServiceImpl extends ServiceImpl<AppCustomerMapper, AppCustomer> implements AppCustomerService {
    @Autowired
    private AppCustomerAuthMapper appCustomerAuthMapper;
    @Autowired
    private CustomerWalletMapper customerWalletMapper;
    @Autowired
    private CustomerInviteMapper customerInviteMapper;
    @Autowired
    private CustomerSettingsMapper customerSettingsMapper;
    @Autowired
    private SysSettingsMapper sysSettingsMapper;
    @Autowired
    private TWalletJournalMapper tWalletJournalMapper;
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private AttentionMapper attentionMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RedisService redisService;
    @Autowired
    private AuthUtil authUtil;
    @Autowired
    private Executor asyncExecutor;
    @Autowired
    private HomeBrowsedMapper homeBrowsedMapper;

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public AddAppCustomerVo add(AddAppCustomerDto dto) {
        long usernameCount = count(new LambdaQueryWrapper<AppCustomer>().eq(AppCustomer::getUsername, dto.getUsername()));
        Asserts.fail(usernameCount > 0, ResultCode.USERNAME_REPETITION);

        LambdaQueryWrapper<SysSettings> ssQuery = new LambdaQueryWrapper<>();
        SysSettings sysSettings = sysSettingsMapper.selectOne(ssQuery);
        Asserts.fail(null == sysSettings, ResultCode.DATA_NOT_EXISTS);
        Integer registerRewardInt = sysSettings.getRegisterReward();
        BigDecimal registerReward = new BigDecimal(registerRewardInt);
        Integer inviteRewardInt = sysSettings.getInviteReward();
        BigDecimal inviteReward = new BigDecimal(inviteRewardInt);

        AppCustomer inviter = null;
        String inviteeCode = dto.getInviteCode();
        if(StringUtils.isNotBlank(inviteeCode)) {
            inviter = getOne(new LambdaQueryWrapper<AppCustomer>().eq(AppCustomer::getInviteCode, dto.getInviteCode()));
            Asserts.fail(inviter==null, ResultCode.INVALIDATE_INVITE_CODE);
        }

        String selfInviteCode = RandomValueUtils.capitalNumber(6);
        LambdaQueryWrapper<AppCustomer> inviteCodeQuery = new LambdaQueryWrapper<AppCustomer>().eq(AppCustomer::getInviteCode, selfInviteCode);
        long inviteCount = baseMapper.selectCount(inviteCodeQuery);
        while(inviteCount>0){
            inviteCodeQuery.clear();
            selfInviteCode = RandomValueUtils.capitalNumber(6);
            inviteCodeQuery.eq(AppCustomer::getInviteCode, selfInviteCode);
            inviteCount = baseMapper.selectCount(inviteCodeQuery);
        }

        AppCustomer appCustomer = new AppCustomer();
        BeanUtil.copyProperties(dto, appCustomer);
        appCustomer.setNickname(dto.getUsername());
        appCustomer.setInviteCode(selfInviteCode);
        appCustomer.setState(StateEnum.NORMAL.getCode());
        appCustomer.setAvatarUrl(CommonConstants.DEFAULT_AVATAR_URL);

        save(appCustomer);
        Long customerId = appCustomer.getId();

        String bscAddr = null;
        String pr = null;
        String mnemonics = null;
        // 生成钱包并保存地址
        ResponseEntity<CommonResult> gwResp = restTemplate.postForEntity(UrlConstants.API_GENERATE_WALLET_URL, null, CommonResult.class);
        if (gwResp.getStatusCode() == HttpStatus.OK) {
            CommonResult result = gwResp.getBody();
            if(result!=null){
                Map data = (Map)result.getData();
                bscAddr = (String)data.get("eth_address");
                pr = (String)data.get("pr");
                mnemonics = (String)data.get("mnemonics");
            }else{
                log.warn("生成钱包失败");
                throw new ApiException(ResultCode.GENERATE_WALLET_FAILED);
            }
        } else {
            log.warn("生成钱包失败");
            throw new ApiException(ResultCode.GENERATE_WALLET_FAILED);
        }

        AppCustomerAuth appCustomerAuth = new AppCustomerAuth();
        appCustomerAuth.setCustomerId(customerId);
        appCustomerAuth.setPassword(BCrypt.hashpw(dto.getPassword()));
        appCustomerAuth.setTxPassword("123456");
        appCustomerAuth.setMnemonics(mnemonics);
        appCustomerAuth.setPr(pr);
        appCustomerAuthMapper.insert(appCustomerAuth);

        // 注册奖励
        TWalletJournal registerJournal = TWalletJournal.builder()
                .customerId(customerId)
                .journalType(JournalTypeEnum.REGISTER_REWARD.getCode())
                .beforeBalance(BigDecimal.ZERO)
                .amount(registerReward)
                .afterBalance(registerReward)
                .currency(CurrencyEnum.BCPC.getCode())
                .direction(DirectionEnum.DEBIT.getCode())
                .journalState(JournalStateEnum.FINISHED.getCode())
                .remark("注册奖励")
                .createdId(customerId)
                .updatedId(customerId)
                .build();
        tWalletJournalMapper.insert(registerJournal);

        CustomerWallet customerWallet = CustomerWallet.builder().build();
        customerWallet.setCustomerId(customerId);
        customerWallet.setBscAddr(bscAddr);
        customerWallet.setBcpc(registerReward);
        customerWalletMapper.insert(customerWallet);

        CustomerSettings settings = new CustomerSettings();
        settings.setCustomerId(customerId);
        settings.setAcceptGroupInvite(AcceptGroupInvite.ACCEPT.getCode());
        settings.setClearGroupExit(ClearGroupExit.DELETE.getCode());
        settings.setSilence(TFEnum.FALSE.getCode());
        customerSettingsMapper.insert(settings);

        if(inviter!=null){
            Long inviterId = inviter.getId();
            CustomerInvite customerInvite = new CustomerInvite();
            customerInvite.setInviterId(inviterId);
            customerInvite.setInviteeId(customerId);

            String invitePath = buildPath(inviterId);
            customerInvite.setPath(invitePath);
            customerInviteMapper.insert(customerInvite);

            // 邀请奖励
            LambdaQueryWrapper<CustomerWallet> ivwQuery = new LambdaQueryWrapper<>();
            ivwQuery.eq(CustomerWallet::getCustomerId, inviterId);
            CustomerWallet inviterWallet = customerWalletMapper.selectOne(ivwQuery);
            BigDecimal inviteBefore = inviterWallet.getBcpc();
            BigDecimal inviteAfter = inviteBefore.add(inviteReward);

            TWalletJournal inviteJournal = TWalletJournal.builder()
                    .customerId(inviterId)
                    .journalType(JournalTypeEnum.INVITE_REWARD.getCode())
                    .beforeBalance(inviteBefore)
                    .amount(inviteReward)
                    .afterBalance(inviteAfter)
                    .currency(CurrencyEnum.BCPC.getCode())
                    .direction(DirectionEnum.DEBIT.getCode())
                    .journalState(JournalStateEnum.FINISHED.getCode())
                    .remark("邀请奖励")
                    .createdId(customerId)
                    .updatedId(customerId)
                    .build();
            tWalletJournalMapper.insert(inviteJournal);

            CustomerWallet inviteUpdateWallet = CustomerWallet.builder()
                    .id(inviterWallet.getId())
                    .bcpc(inviteAfter)
                    .build();
            customerWalletMapper.updateById(inviteUpdateWallet);
        }
        AddAppCustomerVo vo = new AddAppCustomerVo();
        vo.setMnemonics(appCustomerAuth.getMnemonics());
        return vo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public boolean edit(UpdateAppCustomerDto dto) {
        Asserts.fail(null == dto.getId(),ResultCode.VALIDATE_FAILED);
        Long customerId = dto.getId();
        String nickname = dto.getNickname();
        Long loginId = authUtil.getUserId();
        // app客户只能编辑自己的信息
        Asserts.fail(!loginId.equals(customerId),ResultCode.VALIDATE_FAILED);

        AppCustomer appCustomer = baseMapper.selectById(customerId);
        Asserts.fail(null == appCustomer,ResultCode.DATA_NOT_EXISTS);

        if(StringUtils.isNotBlank(nickname)) {
            long count = count(new LambdaQueryWrapper<AppCustomer>()
                    .eq(AppCustomer::getNickname, nickname)
                    .ne(AppCustomer::getId, customerId));
            Asserts.fail(count > 0, ResultCode.USERNAME_REPETITION);

            count = count(new LambdaQueryWrapper<AppCustomer>()
                    .eq(AppCustomer::getUsername, nickname)
                    .ne(AppCustomer::getId, customerId));
            Asserts.fail(count > 0, ResultCode.USERNAME_REPETITION);

            appCustomer.setNickname(dto.getNickname());
        }

        String intro = dto.getIntro();
        if(StringUtils.isNotBlank(intro)){
            appCustomer.setIntro(intro);
        }

        String avatarUrl = dto.getAvatarUrl();
        if(StringUtils.isNotBlank(avatarUrl)){
            appCustomer.setAvatarUrl(avatarUrl);
        }

        if(StringUtils.isNotBlank(nickname) || StringUtils.isNotBlank(intro) || StringUtils.isNotBlank(avatarUrl)) {
            updateById(appCustomer);
        }

        String acceptGroupInvite = dto.getAcceptGroupInvite();
        String clearGroupExit = dto.getClearGroupExit();
        CustomerSettings customerSettings = customerSettingsMapper.selectOne(new LambdaQueryWrapper<CustomerSettings>().eq(CustomerSettings::getCustomerId, customerId));
        Long settingsId = customerSettings.getCustomerId();
        CustomerSettings updateSetting = new CustomerSettings();
        updateSetting.setId(settingsId);
        updateSetting.setCustomerId(customerId);
        if(acceptGroupInvite!=null){
            updateSetting.setAcceptGroupInvite(acceptGroupInvite);
        }
        if(clearGroupExit!=null){
            updateSetting.setClearGroupExit(clearGroupExit);
        }
        if(acceptGroupInvite!=null || clearGroupExit!=null){
            customerSettingsMapper.updateById(updateSetting);
        }
        return true;
    }

    @Override
    public ProfileVo profile(Long id) {
        AppCustomer appCustomer = baseMapper.selectById(id);
        Asserts.fail(null == appCustomer,ResultCode.NOT_EXIST_CUSTOMER);

        AppCustomerAuth appCustomerAuth = appCustomerAuthMapper.selectOne(new LambdaQueryWrapper<AppCustomerAuth>().eq(AppCustomerAuth::getCustomerId, id));
        Asserts.fail(null == appCustomerAuth,ResultCode.NOT_EXIST_CUSTOMER);

        CustomerSettings customerSettings = customerSettingsMapper.selectOne(new LambdaQueryWrapper<CustomerSettings>().eq(CustomerSettings::getCustomerId, id));
        Asserts.fail(null == customerSettings,ResultCode.NOT_EXIST_CUSTOMER);

        CustomerWallet customerWallet = customerWalletMapper.selectOne(new LambdaQueryWrapper<CustomerWallet>().eq(CustomerWallet::getCustomerId, id));
        Asserts.fail(null == customerWallet,ResultCode.NOT_EXIST_WALLET);

        String key = CacheConstants.CACHE_ACTIVE_USER + id;
        Object cachedActiveTimeObj = redisService.get(key);
        Long cachedActiveTime = 0L;
        if(cachedActiveTimeObj instanceof Integer){
            cachedActiveTime = ((Integer)cachedActiveTimeObj).longValue();
        }
        if(cachedActiveTimeObj instanceof Long){
            cachedActiveTime = (Long)cachedActiveTimeObj;
        }
        Long loginId = authUtil.getUserId();

        // 查询帖子数(包括转发数)
        LambdaQueryWrapper<Article> articleQuery = new LambdaQueryWrapper<>();
        articleQuery.and(i->i.eq(Article::getCreatedId, id).or().eq(Article::getForwardId, id));
        Long articleCnt = articleMapper.selectCount(articleQuery);

        // 查询我关注的
        LambdaQueryWrapper<Attention> attQuery = new LambdaQueryWrapper<>();
        attQuery.eq(Attention::getCreatedId, id);
        Long followCnt = attentionMapper.selectCount(attQuery);

        // 查询我的粉丝
        LambdaQueryWrapper<Attention> fansQuery = new LambdaQueryWrapper<>();
        fansQuery.eq(Attention::getFollowedId, id);
        Long fansCnt = attentionMapper.selectCount(fansQuery);

        asyncExecutor.execute(()->{
            HomeBrowsed hb = new HomeBrowsed();
            hb.setBrowserId(loginId);
            hb.setCustomerId(id);
            homeBrowsedMapper.insert(hb);
        });

        ProfileVo profileVo = new ProfileVo();
        profileVo.setCustomerId(id);
        profileVo.setUsername(appCustomer.getUsername());
        profileVo.setNickname(appCustomer.getNickname());
        profileVo.setIntro(appCustomer.getIntro());
        profileVo.setAvatarUrl(appCustomer.getAvatarUrl());
        profileVo.setInviteCode(appCustomer.getInviteCode());
        profileVo.setLastAccessTime(cachedActiveTime);
        profileVo.setWithdrawable(customerWallet.getWithdrawable());
        profileVo.setBscAddress(customerWallet.getBscAddr());

        profileVo.setArticleCnt(articleCnt);
        profileVo.setFollowCnt(followCnt);
        profileVo.setFansCnt(fansCnt);
        return profileVo;
    }

    public String buildPath(Long customerId){
        List<CustomerInvitePathVo> invitePathVOList = customerInviteMapper.findInvitePathById(customerId);
        String invitePath = "";
        if(!CollectionUtils.isEmpty(invitePathVOList)) {
            invitePath = invitePathVOList.stream().map(ip->ip.getInviterId().toString()).collect(Collectors.joining(","));
            invitePath = customerId+","+invitePath;
        }else{
            invitePath = customerId.toString();
        }
        return invitePath;
    }
}
